package com.mkvld.appietijdenv2.data;

import java.io.Serializable;
import java.util.Calendar;
import java.util.Date;
import java.util.List;
import java.util.Locale;

import org.json.JSONObject;

/**
 * 
 * 
 * BreakTime is the amount of hours of unpayed breaks
 * WorkTime is the amount of hours that are actually payed
 * @author Remco
 *
 */
public class Shift implements Serializable, Comparable<Shift> {
	private static final long serialVersionUID = 306877494548261423L;
	private int year, week, day;
	private double start, end;
	private long googleId;
	private ShiftType type;
	
	public Shift(long start, long end) {
		type = ShiftType.PRODUCTIVE;
		Calendar c = Calendar.getInstance(Utils.locale);
		c.setTimeInMillis(start);
		year = c.get(Calendar.YEAR);
		week = c.get(Calendar.WEEK_OF_YEAR) - 1;
		day = (c.get(Calendar.DAY_OF_WEEK) + 5) % 7;
		this.start = (double)c.get(Calendar.HOUR_OF_DAY) + (double)c.get(Calendar.MINUTE) / 60;
		c.setTimeInMillis(end);
		this.end = (double)c.get(Calendar.HOUR_OF_DAY) + (double)c.get(Calendar.MINUTE) / 60;
	}
	
	// This constructor is used for creating a new Shift in the NewShiftDialog
	public Shift(int year, int week, int day) {
		this.year = year;
		this.week = week;
		this.day = day;
		Calendar c = Calendar.getInstance(new Locale("NL", "nl"));
		this.start = c.get(Calendar.HOUR_OF_DAY);
		if (start > 20)
			end = 23;
		else
			end = start + 3;
		googleId = -1;
	}
	
	// This constructor is used to create a new Shift from a database entry
	public Shift(ShiftType type, int year, int week, int day, double start, double end, long googleId) {
		this.type = type;
		this.year = year;
		this.week = week;
		this.day = day;
		this.start = start;
		this.end = end;
		this.googleId = googleId;
	}
	
	public Shift(int year, int week, int day, double start, double end) {
		this(ShiftType.PRODUCTIVE, year, week, day, start, end, -1);
	}
	
	public Date getDate() {
		Calendar cal = Calendar.getInstance(Utils.locale);
		cal.set(Calendar.YEAR, year);
		
		cal.set(Calendar.DAY_OF_WEEK, (day + 2) % 7);
		cal.set(Calendar.WEEK_OF_YEAR, week + 1);
		cal.set(Calendar.HOUR_OF_DAY, 0);
		cal.set(Calendar.MINUTE, 0);
		cal.set(Calendar.SECOND, 0);
		cal.set(Calendar.MILLISECOND, 0);		
		return cal.getTime();
	}
	
	private double getBreakTime() {
		List<Break> breaks = Break.getBreaks(start, end);
		double time = 0;
		for (Break break1 : breaks) {
			if (!break1.isPayed())
				time += (double)break1.getMin() / 60;
		}
		return time;
	}
	
	public double getWorkTime() {
		if (type == ShiftType.PRODUCTIVE)
			return (end - start) - getBreakTime();
		else
			return 0;
	}
	
	@Override
	public String toString() {
		return String.format(new Locale("NL", "nl"), 
				"Dag: %d Week: %d Jaar: %d Tijd: %s-%s", 
				day, week, year, getStartString(), getEndString());
	}
	
	public int getYear() {
		return year;
	}

	public void setYear(int year) {
		this.year = year;
	}

	public int getWeek() {
		return week;
	}

	public void setWeek(int week) {
		this.week = week;
	}

	public int getDay() {
		return day;
	}

	public void setDay(int day) {
		this.day = day;
	}

	public double getStart() {
		return start;
	}

	public void setStart(double start) {
		this.start = start;
	}

	public double getEnd() {
		return end;
	}

	public void setEnd(double end) {
		this.end = end;
	}

	public long getGoogleId() {
		return googleId;
	}

	public void setGoogleId(long googleId) {
		this.googleId = googleId;
	}

	public String getStartString() {
		return Utils.timeToString(start);
	}
	
	public String getEndString() {
		return Utils.timeToString(end);
	}
	
	public void setType(ShiftType type) {
		this.type = type;
	}

	public ShiftType getType() {
		if (type == null)
			return ShiftType.PRODUCTIVE;
		return type;
	}
	
	@Override
	public boolean equals(Object object) {
		try {
			Shift shift = (Shift)object;
			return shift.year == year
					&& shift.week == week
					&& shift.day == day
					&& shift.start == start
					&& shift.end == end;
		} catch (ClassCastException e) {
			return false;
		}
	}
	
	public boolean conflict(Shift shift) {
		return shift.year != year
				|| shift.week != week
				|| shift.day != day
				|| (shift.start >= start && shift.start < end)
				|| (shift.end > start && shift.end <= end);
	}

	public void makeEndValid() {
		if (start >= end) {
			if (start > 22)
				end = 23;
			else
				end = start + 1;
		}
	}
	public void makeStartValid() {
		if (start >= end) {
			if (end == 0)
				start = 23;
			else if (end < 1)
				start = 0;
			else
				start = end - 1;
		}
	}
	
	public Allowance getAllowance() {
		Allowance allowance = new Allowance();
		if (type == ShiftType.PRODUCTIVE) {
			if (day == 6)
				allowance.fixedDayAllowance = getWorkTime();
			else if (day == 5) {
				allowance.dayAllowance = (end > 18f) ? (end - 18f) * 0.5f : 0;
			} else {
				if (end >= 21)
					allowance.dayAllowance = (1f/3f) + (end - 21f) * 0.5f;
				else if (end > 20)
					allowance.dayAllowance = (end - 20f) / 3f;
				else 
					allowance.dayAllowance = 0;
			}
		}
		return allowance;
	}
	
	@Override
	public Shift clone() {
		return new Shift(type, year, week, day, start, end, googleId);
	}
	
	public static class Allowance implements Serializable {
		private static final long serialVersionUID = 1079174662913667101L;
		public double fixedDayAllowance, dayAllowance, weekAllowance, periodAllowance;
		
		public Allowance() {
			fixedDayAllowance = 0;
			dayAllowance = 0;
			weekAllowance = 0;
			periodAllowance = 0;
		}
		
		public void add(Allowance allowance) {
			fixedDayAllowance += allowance.fixedDayAllowance;
			dayAllowance += allowance.dayAllowance;
			weekAllowance += allowance.weekAllowance;
			periodAllowance += allowance.periodAllowance;
		}

		public double getTotal() {
			return fixedDayAllowance + dayAllowance + weekAllowance + periodAllowance;
		}
	}

	@Override
	public int compareTo(Shift another) {
		long time = ((year * 52 + week) * 7 + day) * 24 + (int)start;
		long otherTime = ((another.year * 52 + another.week) * 7 + another.day) * 24 + (int)another.start;
		if (time < otherTime)
			return -1;
		else if (time == otherTime)
			return 0;
		else 
			return 1;
	}
	
	public enum ShiftType {
		PRODUCTIVE (0,0), 
		ADV (1, -1), 
		VACATION (2, -1), 
		OBV (3, 1), 
		TVT (4, -1), 
		SICK (5,2), 
		BD (6, 3);
		
		private final int index;
		private final int indexKPT;
		
		private ShiftType(int index, int indexKPT) {
			this.index = index;
			this.indexKPT = indexKPT;
		}
		
		public int getIndex(boolean kpt) {
			return kpt ? indexKPT : index;
		}
	}

	public JSONObject toJSON() {
		// TODO implemetn toJSON
		return null;
	};
}
